﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
using System.Linq;

namespace Slex.Timeline {
    public struct TlArgument {
        public bool autoReverse;
        public TimeSpan? beginTime;
        public Duration duration;
        public IEasingFunction easingFunction;
        public FillBehavior fillBehavior;
        public RepeatBehavior repeatBehavior;
        public CartoonType cartoonType;
        public double SpeedRatio;
        public TlArgument(double durationv, double repeat = 1, bool rever = false, IEasingFunction fun = null, double bgtime = 0, CartoonType ct = CartoonType.None) {
            autoReverse = rever; beginTime = TimeSpan.FromSeconds(bgtime);
            duration = new Duration(TimeSpan.FromMilliseconds(durationv == 0d ? 500 : durationv));
            fillBehavior = System.Windows.Media.Animation.FillBehavior.Stop;
            repeatBehavior = new RepeatBehavior(repeat);
            SpeedRatio = 1; easingFunction = fun; cartoonType = ct;
        }
    }
    public enum TransformType { Translate, Rotate, Scale, Skew, Coposition }
    public enum BrushType { Background, Foreground, Fill, Stroke, BorderBrush }
    public enum AxisType { x, y, z }
    public enum ErgodicType { all, column, row }
    public enum NumberByType { row, column }
    public enum CartoonType { None, Transform, Projection, Opacity, Width, Height, Background, Fill, Stroke, Foreground }
    public class CustomEasing : EasingFunctionBase {
        Func<double, double> f1;
        public CustomEasing(Func<double, double> easeInCore) {
            this.f1 = easeInCore;
        }
        protected override double EaseInCore(double normalizedTime) {
            if (this.f1 != null)
                return this.f1.Invoke(normalizedTime);
            else
                return normalizedTime;
        }
    }
    public class MatrixHelp : DependencyObject {
        double[,] fMatrix;
        public const int rlen = 4;
        public const int clen = 4;
        public Matrix3DProjection pMatrix3DProjection { get; private set; }
        public MatrixHelp pUnit { get { this.SetToUnit(); return this; } }
        public MatrixHelp pZero { get { this.SetToZero(); return this; } }
        public MatrixHelp pOnes { get { this.SetToOnes(); return this; } }
        public MatrixHelp() {
            fMatrix = new double[rlen, clen];
            this.pMatrix3DProjection = new Matrix3DProjection();
        }
        #region basic operator
        protected double this[int i, int j] {
            get { return this.fMatrix[i, j]; }
            set { this.fMatrix[i, j] = value; }
        }
        protected IEnumerable<Tuple<double, byte, byte>> Ergodic(ErgodicType et, byte point = 0) {
            if (point < 0 || point > 3) {
                throw new Exception("指定行列数超出范围，范围在0到4之间");
            }
            switch (et) {
                case ErgodicType.all:
                    for (byte i = 0; i < rlen; i++) {
                        for (byte j = 0; j < clen; j++) {
                            yield return Tuple.Create(this.fMatrix[i, j], i, j);
                        }
                    }
                    break;
                case ErgodicType.column:
                    for (byte i = 0; i < rlen; i++) {
                        yield return Tuple.Create(this.fMatrix[i, point], i, point);
                    }
                    break;
                case ErgodicType.row:
                    for (byte j = 0; j < clen; j++) {
                        yield return Tuple.Create(this.fMatrix[point, j], point, j);
                    }
                    break;
                default:
                    break;
            }
        }
        //
        protected void SetToZero() {
            for (int i = 0; i < rlen; i++) {
                for (int j = 0; j < clen; j++) {
                    fMatrix[i, j] = 0;
                }
            }
        }
        protected void SetToOnes() {
            for (int i = 0; i < rlen; i++) {
                for (int j = 0; j < clen; j++) {
                    fMatrix[i, j] = 1;
                }
            }
        }
        protected void SetToUnit() {
            for (int i = 0; i < rlen; i++) {
                for (int j = 0; j < clen; j++) {
                    if (i == j) {
                        fMatrix[i, j] = 1;
                        continue;
                    }
                    fMatrix[i, j] = 0;
                }
            }
        }
        protected void SetToTranspose() {
            double[,] tempmatrix = new double[rlen, clen];
            for (int i = 0; i < rlen; i++) {
                for (int j = 0; j < clen; j++) {
                    tempmatrix[j, i] = fMatrix[i, j];
                }
            }
            fMatrix = null;
            fMatrix = tempmatrix;
        }
        //

        public static implicit operator Matrix(MatrixHelp d3) {
            Matrix r = default(Matrix);
            if (d3 != null && d3.fMatrix != null)
                r = new Matrix(
                    d3.fMatrix[0, 0], d3.fMatrix[0, 1], d3.fMatrix[1, 0], d3.fMatrix[1, 1],
                    d3.fMatrix[3, 0], d3.fMatrix[3, 1]
                    );
            return r;
        }

        public static implicit operator Matrix3D(MatrixHelp d3) {
            Matrix3D r = default(Matrix3D);
            if (d3 != null && d3.fMatrix != null)
                r = new Matrix3D(
                    d3.fMatrix[0, 0], d3.fMatrix[0, 1], d3.fMatrix[0, 2], d3.fMatrix[0, 3],
                    d3.fMatrix[1, 0], d3.fMatrix[1, 1], d3.fMatrix[1, 2], d3.fMatrix[1, 3],
                    d3.fMatrix[2, 0], d3.fMatrix[2, 1], d3.fMatrix[2, 2], d3.fMatrix[2, 3],
                    d3.fMatrix[3, 0], d3.fMatrix[3, 1], d3.fMatrix[3, 2], d3.fMatrix[3, 3]
                    );
            return r;
        }
        public static implicit operator MatrixHelp(Matrix3D m3) {
            MatrixHelp r = default(MatrixHelp);
            if (m3 != default(Matrix3D)) {
                r = new MatrixHelp {
                    fMatrix = new double[MatrixHelp.rlen, MatrixHelp.clen],
                };
                r.fMatrix[0, 0] = m3.M11;
                r.fMatrix[0, 1] = m3.M12;
                r.fMatrix[0, 2] = m3.M13;
                r.fMatrix[0, 3] = m3.M14;

                r.fMatrix[1, 0] = m3.M21;
                r.fMatrix[1, 1] = m3.M22;
                r.fMatrix[1, 2] = m3.M23;
                r.fMatrix[1, 3] = m3.M24;

                r.fMatrix[2, 0] = m3.M31;
                r.fMatrix[2, 1] = m3.M32;
                r.fMatrix[2, 2] = m3.M33;
                r.fMatrix[2, 3] = m3.M34;

                r.fMatrix[3, 0] = m3.OffsetX;
                r.fMatrix[3, 1] = m3.OffsetY;
                r.fMatrix[3, 2] = m3.OffsetZ;
                r.fMatrix[3, 3] = m3.M44;
            }
            return r;
        }
        public static MatrixHelp operator +(MatrixHelp m1, MatrixHelp m2) {
            for (int i = 0; i < MatrixHelp.rlen; i++) {
                for (int j = 0; j < MatrixHelp.clen; j++) {
                    m1.fMatrix[i, j] += m2.fMatrix[i, j];
                }
            }
            return m1;
        }
        public static MatrixHelp operator -(MatrixHelp m1, MatrixHelp m2) {
            for (int i = 0; i < MatrixHelp.rlen; i++) {
                for (int j = 0; j < MatrixHelp.clen; j++) {
                    m1.fMatrix[i, j] -= m2.fMatrix[i, j];
                }
            }
            return m1;
        }
        public static MatrixHelp operator *(MatrixHelp m1, MatrixHelp m2) {
            MatrixHelp d3 = new MatrixHelp();
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    d3[i, j] = m1[i, 0] * m2[0, j] + m1[i, 1] * m2[1, j] + m1[i, 2] * m2[2, j] + m1[i, 3] * m2[3, j];
                }
            }
            return d3;
        }
        public static MatrixHelp operator /(MatrixHelp m1, MatrixHelp m2) {
            MatrixHelp d3 = default(MatrixHelp);
            return d3;
        }
        #endregion
        #region operator
        public MatrixHelp Interlace(double s, double t, AxisType at) {
            this.SetToUnit();
            switch (at) {
                case AxisType.x:
                    this.fMatrix[2, 0] = s;
                    this.fMatrix[2, 1] = t;
                    break;
                case AxisType.z:
                    this.fMatrix[1, 0] = s;
                    this.fMatrix[1, 2] = t;
                    break;
                case AxisType.y:
                    this.fMatrix[0, 1] = s;
                    this.fMatrix[0, 2] = t;
                    break;
                default:
                    break;
            }
            return this;
        }
        public void ResetMatrix() {
            this.fMatrix = new double[rlen, clen];
        }
        public MatrixHelp Scale(double scalefactor) {
            this.SetToZero();
            this.fMatrix[0, 0] = scalefactor;
            this.fMatrix[1, 1] = scalefactor;
            this.fMatrix[2, 2] = scalefactor;
            this.fMatrix[3, 3] = 1;
            return this;
        }
        public MatrixHelp Scale(double dx, double dy, double dz) {
            this.SetToZero();
            this.fMatrix[0, 0] = dx;
            this.fMatrix[1, 1] = dy;
            this.fMatrix[2, 2] = dz;
            this.fMatrix[3, 3] = 1;
            return this;
        }
        public MatrixHelp Spin(double angle, AxisType st) {
            this.SetToUnit();
            switch (st) {
                case AxisType.x:
                    this.fMatrix[1, 1] = Math.Cos(angle);
                    this.fMatrix[1, 2] = -Math.Sin(angle);
                    this.fMatrix[2, 1] = Math.Sin(angle);
                    this.fMatrix[2, 2] = Math.Cos(angle);
                    break;
                case AxisType.y:
                    this.fMatrix[0, 0] = Math.Cos(angle);
                    this.fMatrix[0, 2] = Math.Sin(angle);
                    this.fMatrix[2, 0] = -Math.Sin(angle);
                    this.fMatrix[2, 2] = Math.Cos(angle);
                    break;
                case AxisType.z:
                    this.fMatrix[0, 0] = Math.Cos(angle);
                    this.fMatrix[0, 1] = -Math.Sin(angle);
                    this.fMatrix[1, 0] = Math.Sin(angle);
                    this.fMatrix[1, 1] = Math.Cos(angle);
                    break;
                default:
                    break;
            }
            return this;
        }
        public MatrixHelp Translation(double x, double y, double z) {
            this.SetToUnit();
            this.fMatrix[3, 0] = x;
            this.fMatrix[3, 1] = y;
            this.fMatrix[3, 2] = z;
            return this;
        }
        public void Update() {
            this.pMatrix3DProjection.ProjectionMatrix = this;
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string pn) {
            if (this.PropertyChanged != null) {
                this.PropertyChanged.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(pn));
            }
        }
        #endregion
        //
        #region extends
        /// <summary>
        /// 数乘
        /// </summary>
        /// <param name="v"></param>
        public void NumberBy(double v) {
            for (int i = 0; i < rlen; i++) {
                for (int j = 0; j < clen; j++) {
                    this.fMatrix[i, j] *= v;
                }
            }
        }
        /// <summary>
        /// 数乘某行或列
        /// </summary>
        /// <param name="v"></param>
        public void NumberBy(double v, byte rc, NumberByType nbt) {
            switch (nbt) {
                case NumberByType.row:
                    for (int i = 0; i < clen; i++) {
                        this[rc, i] *= v;
                    }
                    break;
                case NumberByType.column:
                    for (int i = 0; i < rlen; i++) {
                        this[i, rc] *= v;
                    }
                    break;
                default:
                    break;
            }
        }
        #endregion
    }
    public class Entityx {
        public string groupName;
        public string groupIndex;
        public int groupIdx;
        bool? isDouble;
        public double Valued {
            get { var _value = Da.To == null ? 0 : Da.To.Value; return _value; }
            set { Da.To = value; isDouble = true; }
        }
        public Color Valuec {
            get { var _value = Ca.To == null ? Colors.Transparent : Ca.To.Value; return _value; }
            set { Ca.To = value; isDouble = false; }
        }
        public DoubleAnimation Da { get; set; }
        public ColorAnimation Ca { get; set; }
        public Entityx() {
            Da = new DoubleAnimation();
            Ca = new ColorAnimation();
        }
        public void SetArgument(TlArgument tla) {
            if (isDouble != null) {
                if (isDouble.Value) {
                    Da.AutoReverse = tla.autoReverse;
                    Da.BeginTime = tla.beginTime;
                    Da.Duration = tla.duration;
                    Da.EasingFunction = tla.easingFunction;
                    Da.FillBehavior = tla.fillBehavior;
                    Da.RepeatBehavior = tla.repeatBehavior;
                    Da.SpeedRatio = tla.SpeedRatio;
                } else {
                    Ca.AutoReverse = tla.autoReverse;
                    Ca.BeginTime = tla.beginTime;
                    Ca.Duration = tla.duration;
                    Ca.EasingFunction = tla.easingFunction;
                    Ca.FillBehavior = tla.fillBehavior;
                    Ca.RepeatBehavior = tla.repeatBehavior;
                    Ca.SpeedRatio = tla.SpeedRatio;
                }
            }
        }
        public void SetEasing(Func<double, double> fun) {
            if (isDouble != null) {
                if (isDouble.Value) { Da.EasingFunction = new CustomEasing(fun); } else { Ca.EasingFunction = new CustomEasing(fun); }
            }
        }
        public static implicit operator System.Windows.Media.Animation.Timeline(Entityx x) {
            if (x.isDouble != null && x.isDouble.Value) { return x.Da; } else if (x.isDouble != null && !x.isDouble.Value) { return x.Ca; } else { throw new Exception("实体设置值之前不能被载入Storyboard"); }
        }
    }
    public class EntityxsAction {
        Entityx[] exs;
        public Storyboard Storyboard { get; set; }
        public EntityxsAction(Entityx[] exs) {
            this.exs = exs;
        }
        public Entityx this[int idx] { get { return exs[idx]; } }
        public Entityx[] this[string grounp] {
            get {
                return (from item in this.exs where item.groupName == grounp select item).ToArray();
            }
        }
        public Entityx Get(Entityx[] groupx, string groupIndex) { return groupx.Single(x => x.groupIndex == groupIndex); }
        public Entityx Get(Entityx[] groupx, int groupIdx) { return groupx.Single(x => x.groupIdx == groupIdx); }
    }
    /*
     * transforms,projection,opacity,fill,background foreground animation
     */
    public static class Cartoon {
        #region method sugar
        public static TlArgument Geta(double durationv, double repeat = 1, bool rever = false, Func<double, double> fun = null, double bgtime = 0, CartoonType ct = CartoonType.None) {
            var tla = new TlArgument(durationv, repeat, rever, null, bgtime, ct);
            tla.easingFunction = new CustomEasing(fun);
            return tla;
        }
        public static Transform GetTrf(double x, double y) { return new TranslateTransform { X = x, Y = y }; }
        public static Transform GetTrf(double x, double y, TransformType tt) {
            var r = default(Transform);
            switch (tt) {
                case TransformType.Translate:
                    break;
                case TransformType.Rotate:
                    break;
                case TransformType.Scale:
                    r = new ScaleTransform { ScaleX = x, ScaleY = y };
                    break;
                case TransformType.Skew:
                    r = new SkewTransform { AngleX = x, AngleY = y };
                    break;
                case TransformType.Coposition:
                    r = new CompositeTransform { TranslateX = x, TranslateY = y };
                    break;
                default:
                    break;
            }
            return r;
        }
        public static Transform GetTrf(double x, double y, double cx, double cy, TransformType tt) {
            var r = default(Transform);
            switch (tt) {
                case TransformType.Translate:
                    break;
                case TransformType.Rotate:
                    break;
                case TransformType.Scale:
                    r = new ScaleTransform { ScaleX = x, ScaleY = y, CenterX = cx, CenterY = cy };
                    break;
                case TransformType.Skew:
                    r = new SkewTransform { AngleX = x, AngleY = y, CenterX = cx, CenterY = cy };
                    break;
                case TransformType.Coposition:
                    r = new CompositeTransform { TranslateX = x, TranslateY = y, CenterX = cx, CenterY = cy };
                    break;
                default:
                    break;
            }
            return r;
        }
        public static Transform GetTrf(double rotate) { return new RotateTransform { Angle = rotate }; }
        public static Transform GetTrf(double rotate, double cx, double cy) { return new RotateTransform { Angle = rotate, CenterX = cx, CenterY = cy }; }
        public static PlaneProjection GetPro(double lx, double ly, double lz) { var pp = new PlaneProjection(); pp.LocalOffsetX = lx; pp.LocalOffsetY = ly; pp.LocalOffsetZ = lz; return pp; }
        public static PlaneProjection GetPro(double lx, double ly, double lz, double rx, double ry, double rz) { var pp = new PlaneProjection(); pp.LocalOffsetX = lx; pp.LocalOffsetY = ly; pp.LocalOffsetZ = lz; pp.RotationX = rx; pp.LocalOffsetY = ry; pp.LocalOffsetZ = rz; return pp; }
        #endregion
        public static Storyboard Transformx(this FrameworkElement fe, Transform to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var x = "(FrameworkElement.RenderTransform).(TranslateTransform.X)";
            var y = "(FrameworkElement.RenderTransform).(TranslateTransform.Y)";
            var r = "(FrameworkElement.RenderTransform).(RotateTransform.Angle)";
            var rx = "(FrameworkElement.RenderTransform).(RotateTransform.CenterX)";
            var ry = "(FrameworkElement.RenderTransform).(RotateTransform.CenterY)";
            var scx = "(FrameworkElement.RenderTransform).(ScaleTransform.ScaleX)";
            var scy = "(FrameworkElement.RenderTransform).(ScaleTransform.ScaleY)";
            var sccx = "(FrameworkElement.RenderTransform).(ScaleTransform.CenterX)";
            var sccy = "(FrameworkElement.RenderTransform).(ScaleTransform.CenterY)";
            var skx = "(FrameworkElement.RenderTransform).(SkewTransform.AngleX)";
            var sky = "(FrameworkElement.RenderTransform).(SkewTransform.AngleY)";
            var skcx = "(FrameworkElement.RenderTransform).(SkewTransform.CenterX)";
            var skcy = "(FrameworkElement.RenderTransform).(SkewTransform.CenterY)";
            var ctx = "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateX)";
            var cty = "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateY)";
            var cskx = "(FrameworkElement.RenderTransform).(CompositeTransform.SkewX)";
            var csky = "(FrameworkElement.RenderTransform).(CompositeTransform.SkewY)";
            var cscx = "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleX)";
            var cscy = "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleY)";
            var cr = "(FrameworkElement.RenderTransform).(CompositeTransform.Rotation)";
            var ccx = "(FrameworkElement.RenderTransform).(CompositeTransform.CenterX)";
            var ccy = "(FrameworkElement.RenderTransform).(CompositeTransform.CenterY)";

            if (to is TranslateTransform) {
                var to2 = to as TranslateTransform;
                var da = new DoubleAnimation { To = to2.X, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da2 = new DoubleAnimation { To = to2.Y, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.RenderTransform as TranslateTransform;
                if (from != null) {
                    da.From = from.X;
                    da2.From = from.Y;
                } else {
                    from = new TranslateTransform(); fe.RenderTransform = from;
                }
                Storyboard.SetTargetProperty(da, new PropertyPath(x));
                Storyboard.SetTargetProperty(da2, new PropertyPath(y));
                Storyboard.SetTarget(da, fe); Storyboard.SetTarget(da2, fe);
                sb.Children.Add(da); sb.Children.Add(da2);
            } else if (to is RotateTransform) {
                var to2 = to as RotateTransform;
                var da = new DoubleAnimation { To = to2.Angle, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da2 = new DoubleAnimation { To = to2.CenterX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da3 = new DoubleAnimation { To = to2.CenterY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.RenderTransform as RotateTransform;
                if (from != null) {
                    da.From = from.Angle;
                    da2.From = from.CenterX;
                    da3.From = from.CenterY;
                } else {
                    from = new RotateTransform(); fe.RenderTransform = from;
                }
                Storyboard.SetTargetProperty(da, new PropertyPath(r));
                Storyboard.SetTargetProperty(da2, new PropertyPath(rx));
                Storyboard.SetTargetProperty(da3, new PropertyPath(ry));
                Storyboard.SetTarget(da, fe);
                Storyboard.SetTarget(da2, fe);
                Storyboard.SetTarget(da3, fe);
                sb.Children.Add(da); sb.Children.Add(da2); sb.Children.Add(da3);
            } else if (to is ScaleTransform) {
                var to2 = to as ScaleTransform;
                var da = new DoubleAnimation { To = to2.ScaleX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da4 = new DoubleAnimation { To = to2.ScaleY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da2 = new DoubleAnimation { To = to2.CenterX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da3 = new DoubleAnimation { To = to2.CenterY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.RenderTransform as ScaleTransform;
                if (from != null) {
                    da.From = from.ScaleX;
                    da2.From = from.CenterX;
                    da3.From = from.CenterY;
                    da4.From = from.ScaleY;
                } else {
                    from = new ScaleTransform(); fe.RenderTransform = from;
                }
                Storyboard.SetTargetProperty(da, new PropertyPath(scx));
                Storyboard.SetTargetProperty(da2, new PropertyPath(sccx));
                Storyboard.SetTargetProperty(da3, new PropertyPath(sccy));
                Storyboard.SetTargetProperty(da4, new PropertyPath(scy));
                Storyboard.SetTarget(da, fe);
                Storyboard.SetTarget(da2, fe);
                Storyboard.SetTarget(da3, fe);
                Storyboard.SetTarget(da4, fe);
                sb.Children.Add(da); sb.Children.Add(da2); sb.Children.Add(da3); sb.Children.Add(da4);
            } else if (to is SkewTransform) {
                var to2 = to as SkewTransform;
                var da = new DoubleAnimation { To = to2.AngleX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da4 = new DoubleAnimation { To = to2.AngleY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da2 = new DoubleAnimation { To = to2.CenterX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da3 = new DoubleAnimation { To = to2.CenterY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.RenderTransform as SkewTransform;
                if (from != null) {
                    da.From = from.AngleX;
                    da2.From = from.CenterX;
                    da3.From = from.CenterY;
                    da4.From = from.AngleY;
                } else {
                    from = new SkewTransform(); fe.RenderTransform = from;
                }
                Storyboard.SetTargetProperty(da, new PropertyPath(skx));
                Storyboard.SetTargetProperty(da2, new PropertyPath(skcx));
                Storyboard.SetTargetProperty(da3, new PropertyPath(skcy));
                Storyboard.SetTargetProperty(da4, new PropertyPath(sky));
                Storyboard.SetTarget(da, fe);
                Storyboard.SetTarget(da2, fe);
                Storyboard.SetTarget(da3, fe);
                Storyboard.SetTarget(da4, fe);
                sb.Children.Add(da); sb.Children.Add(da2); sb.Children.Add(da3); sb.Children.Add(da4);

            } else if (to is CompositeTransform) {
                var to2 = to as CompositeTransform;
                var da = new DoubleAnimation { To = to2.TranslateX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da2 = new DoubleAnimation { To = to2.CenterX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da3 = new DoubleAnimation { To = to2.CenterY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da4 = new DoubleAnimation { To = to2.TranslateY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da5 = new DoubleAnimation { To = to2.ScaleX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da6 = new DoubleAnimation { To = to2.ScaleY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da7 = new DoubleAnimation { To = to2.SkewX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da8 = new DoubleAnimation { To = to2.SkewY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da9 = new DoubleAnimation { To = to2.Rotation, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.RenderTransform as CompositeTransform;
                if (from != null) {
                    da.From = from.TranslateX;
                    da4.From = from.TranslateX;
                    da2.From = from.CenterX;
                    da3.From = from.CenterY;
                    da5.From = from.ScaleX;
                    da6.From = from.ScaleY;
                    da7.From = from.SkewX;
                    da8.From = from.SkewY;
                    da9.From = from.Rotation;
                } else {
                    from = new CompositeTransform(); fe.RenderTransform = from;
                }
                Storyboard.SetTargetProperty(da, new PropertyPath(ctx));
                Storyboard.SetTargetProperty(da4, new PropertyPath(cty));
                Storyboard.SetTargetProperty(da5, new PropertyPath(cscx));
                Storyboard.SetTargetProperty(da6, new PropertyPath(cscy));
                Storyboard.SetTargetProperty(da7, new PropertyPath(cskx));
                Storyboard.SetTargetProperty(da8, new PropertyPath(csky));
                Storyboard.SetTargetProperty(da9, new PropertyPath(cr));
                Storyboard.SetTargetProperty(da2, new PropertyPath(ccx));
                Storyboard.SetTargetProperty(da3, new PropertyPath(ccy));
                Storyboard.SetTarget(da, fe); Storyboard.SetTarget(da2, fe);
                Storyboard.SetTarget(da3, fe); Storyboard.SetTarget(da4, fe);
                Storyboard.SetTarget(da5, fe); Storyboard.SetTarget(da6, fe);
                Storyboard.SetTarget(da7, fe); Storyboard.SetTarget(da8, fe);
                Storyboard.SetTarget(da9, fe);
                sb.Children.Add(da);
                sb.Children.Add(da2); sb.Children.Add(da3); sb.Children.Add(da4); sb.Children.Add(da5);
                sb.Children.Add(da6); sb.Children.Add(da7); sb.Children.Add(da8); sb.Children.Add(da9);
            }
            return sb;
        }
        public static Storyboard Projectionx(this FrameworkElement fe, Projection to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var ppcrx = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationX)";
            var ppcry = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationY)";
            var ppcrz = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationZ)";
            var ppgx = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetX)";
            var ppgy = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetY)";
            var ppgz = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetZ)";
            var pplx = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetX)";
            var pply = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetY)";
            var pplz = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetZ)";
            var pprx = "(FrameworkElement.Projection).(PlaneProjection.RotationX)";
            var ppry = "(FrameworkElement.Projection).(PlaneProjection.RotationY)";
            var pprz = "(FrameworkElement.Projection).(PlaneProjection.RotationZ)";
            if (to is PlaneProjection) {
                var to2 = to as PlaneProjection;
                var da_ppcrx = new DoubleAnimation { To = to2.CenterOfRotationX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppcry = new DoubleAnimation { To = to2.CenterOfRotationY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppcrz = new DoubleAnimation { To = to2.CenterOfRotationZ, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppgx = new DoubleAnimation { To = to2.GlobalOffsetX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppgy = new DoubleAnimation { To = to2.GlobalOffsetY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppgz = new DoubleAnimation { To = to2.GlobalOffsetZ, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_pplx = new DoubleAnimation { To = to2.LocalOffsetX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_pply = new DoubleAnimation { To = to2.LocalOffsetY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_pplz = new DoubleAnimation { To = to2.LocalOffsetZ, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_pprx = new DoubleAnimation { To = to2.RotationX, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_ppry = new DoubleAnimation { To = to2.RotationY, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var da_pprz = new DoubleAnimation { To = to2.RotationZ, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var from = fe.Projection as PlaneProjection;
                if (from != null) {
                    da_ppcrx.From = from.CenterOfRotationX;
                    da_ppcry.From = from.CenterOfRotationY;
                    da_ppcrz.From = from.CenterOfRotationZ;
                    da_ppgx.From = from.GlobalOffsetX;
                    da_ppgy.From = from.GlobalOffsetY;
                    da_ppgz.From = from.GlobalOffsetZ;
                    da_pplx.From = from.LocalOffsetX;
                    da_pply.From = from.LocalOffsetY;
                    da_pplz.From = from.LocalOffsetZ;
                    da_pprx.From = from.RotationX;
                    da_ppry.From = from.RotationY;
                    da_pprz.From = from.RotationZ;
                } else {
                    from = new PlaneProjection(); fe.Projection = from;
                }
                Storyboard.SetTargetProperty(da_ppcrx, new PropertyPath(ppcrx));
                Storyboard.SetTargetProperty(da_ppcry, new PropertyPath(ppcry));
                Storyboard.SetTargetProperty(da_ppcrz, new PropertyPath(ppcrz));
                Storyboard.SetTargetProperty(da_ppgx, new PropertyPath(ppgx));
                Storyboard.SetTargetProperty(da_ppgy, new PropertyPath(ppgy));
                Storyboard.SetTargetProperty(da_ppgz, new PropertyPath(ppgz));
                Storyboard.SetTargetProperty(da_pplx, new PropertyPath(pplx));
                Storyboard.SetTargetProperty(da_pply, new PropertyPath(pply));
                Storyboard.SetTargetProperty(da_pplz, new PropertyPath(pplz));
                Storyboard.SetTargetProperty(da_pprx, new PropertyPath(pprx));
                Storyboard.SetTargetProperty(da_ppry, new PropertyPath(ppry));
                Storyboard.SetTargetProperty(da_pprz, new PropertyPath(pprz));
                Storyboard.SetTarget(da_ppcrx, fe); Storyboard.SetTarget(da_ppcry, fe);
                Storyboard.SetTarget(da_ppcrz, fe); Storyboard.SetTarget(da_ppgx, fe);
                Storyboard.SetTarget(da_ppgy, fe); Storyboard.SetTarget(da_ppgz, fe);
                Storyboard.SetTarget(da_pplx, fe); Storyboard.SetTarget(da_pply, fe);
                Storyboard.SetTarget(da_pplz, fe); Storyboard.SetTarget(da_pprx, fe);
                Storyboard.SetTarget(da_ppry, fe); Storyboard.SetTarget(da_pprz, fe);
                sb.Children.Add(da_ppcrx); sb.Children.Add(da_ppcry); sb.Children.Add(da_ppcrz);
                sb.Children.Add(da_ppgx); sb.Children.Add(da_ppgy); sb.Children.Add(da_ppgz);
                sb.Children.Add(da_pplx); sb.Children.Add(da_pply); sb.Children.Add(da_pplz);
                sb.Children.Add(da_pprx); sb.Children.Add(da_ppry); sb.Children.Add(da_pprz);
            }
            return sb;
        }
        //
        public static Storyboard BackgroundFillx(this FrameworkElement fe, SolidColorBrush to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var cb = "(FrameworkElement.Background).(SolidColorBrush.Color)";
            //var cf = "(FrameworkElement.Foreground).(SolidColorBrush.Color)";
            var sf = "(FrameworkElement.Fill).(SolidColorBrush.Color)";
            //var ss = "(FrameworkElement.Stroke).(SolidColorBrush.Color)";
            //var bb = "(FrameworkElement.BorderBrush).(SolidColorBrush.Color)";
            if (fe is Control) {
                var ca_cb = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Control;
                if (con != null && con.Background != null && con.Background is SolidColorBrush) {
                    ca_cb.From = ((SolidColorBrush)con.Background).Color;
                }
                if (con.Background == null) {
                    con.Background = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_cb, new PropertyPath(cb));
                Storyboard.SetTarget(ca_cb, fe);
                sb.Children.Add(ca_cb);
            } else if (fe is Shape) {
                var ca_sf = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Shape;
                if (con != null && con.Fill != null && con.Fill is SolidColorBrush) {
                    ca_sf.From = ((SolidColorBrush)con.Fill).Color;
                }
                if (con.Fill == null) {
                    con.Fill = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_sf, new PropertyPath(sf));
                Storyboard.SetTarget(ca_sf, fe);
                sb.Children.Add(ca_sf);
            } else if (fe is Border) {
                var ca_cbb = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Border;
                if (con != null && con.Background != null && con.Background is SolidColorBrush) {
                    ca_cbb.From = ((SolidColorBrush)con.Background).Color;
                }
                if (con.Background == null) {
                    con.Background = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_cbb, new PropertyPath(cb));
                Storyboard.SetTarget(ca_cbb, fe);
                sb.Children.Add(ca_cbb);
            }
            return sb;
        }
        public static Storyboard ForegroundStrokex(this FrameworkElement fe, SolidColorBrush to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            //var cb = "(FrameworkElement.Background).(SolidColorBrush.Color)";
            var cf = "(FrameworkElement.Foreground).(SolidColorBrush.Color)";
            //var sf = "(FrameworkElement.Fill).(SolidColorBrush.Color)";
            var ss = "(FrameworkElement.Stroke).(SolidColorBrush.Color)";
            var bb = "(FrameworkElement.BorderBrush).(SolidColorBrush.Color)";
            if (fe is Control) {
                var ca_cb = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Control;
                if (con != null && con.Foreground != null && con.Foreground is SolidColorBrush) {
                    ca_cb.From = ((SolidColorBrush)con.Foreground).Color;
                }
                if (con.Foreground == null) {
                    con.Foreground = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_cb, new PropertyPath(cf));
                Storyboard.SetTarget(ca_cb, fe);
                sb.Children.Add(ca_cb);
            } else if (fe is Shape) {
                var ca_sf = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Shape;
                if (con != null && con.Stroke != null && con.Stroke is SolidColorBrush) {
                    ca_sf.From = ((SolidColorBrush)con.Stroke).Color;
                }
                if (con.Stroke == null) {
                    con.Stroke = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_sf, new PropertyPath(ss));
                Storyboard.SetTarget(ca_sf, fe);
                sb.Children.Add(ca_sf);
            } else if (fe is Border) {
                var ca_cbb = new ColorAnimation { To = to.Color, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
                var con = fe as Border;
                if (con != null && con.BorderBrush != null && con.BorderBrush is SolidColorBrush) {
                    ca_cbb.From = ((SolidColorBrush)con.BorderBrush).Color;
                }
                if (con.BorderBrush == null) {
                    con.BorderBrush = new SolidColorBrush();
                }
                Storyboard.SetTargetProperty(ca_cbb, new PropertyPath(bb));
                Storyboard.SetTarget(ca_cbb, fe);
                sb.Children.Add(ca_cbb);
            }
            return sb;
        }
        //
        public static Storyboard Opacityx(this FrameworkElement fe, double to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var da = new DoubleAnimation { To = to, From = fe.Opacity, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
            Storyboard.SetTargetProperty(da, new PropertyPath("FrameworkElement.Opacity"));
            Storyboard.SetTarget(da, fe);
            sb.Children.Add(da);
            return sb;
        }
        public static Storyboard Widthx(this FrameworkElement fe, double to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var da = new DoubleAnimation { To = to, From = fe.Width, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
            Storyboard.SetTargetProperty(da, new PropertyPath("FrameworkElement.Width"));
            Storyboard.SetTarget(da, fe);
            sb.Children.Add(da);
            return sb;
        }
        public static Storyboard Heightx(this FrameworkElement fe, double to, TlArgument sbd = default(TlArgument)) {
            if (sbd.Equals(default(TlArgument))) { sbd = new TlArgument(500); }
            var sb = new Storyboard();
            var da = new DoubleAnimation { To = to, From = fe.Height, AutoReverse = sbd.autoReverse, BeginTime = sbd.beginTime, Duration = sbd.duration, EasingFunction = sbd.easingFunction, FillBehavior = sbd.fillBehavior, RepeatBehavior = sbd.repeatBehavior, SpeedRatio = sbd.SpeedRatio };
            Storyboard.SetTargetProperty(da, new PropertyPath("FrameworkElement.Height"));
            Storyboard.SetTarget(da, fe);
            sb.Children.Add(da);
            return sb;
        }
        //
        public static Storyboard Combine(this Storyboard sb, Storyboard other) {
            var tlst = new List<System.Windows.Media.Animation.Timeline>();
            foreach (var item in other.Children) {
                tlst.Add(item);
            }
            other.Children.Clear();
            foreach (var item in tlst) {
                sb.Children.Add(item);
            }
            return sb;
        }
        //
        public static EntityxsAction Actions(TlArgument tla, params FrameworkElement[] fes) {
            if (fes == null || fes.Length == 0) { return null; }
            var sb = new Storyboard();
            var len = fes.Length;
            var exs = default(Entityx[]);
            if (tla.cartoonType != CartoonType.None && tla.cartoonType != CartoonType.Transform && tla.cartoonType != CartoonType.Projection) {
                exs = new Entityx[len];
                for (int i = 0; i < len; i++) {
                    var b = new Binding();
                    exs[i] = new Entityx();
                    switch (tla.cartoonType) {
                        case CartoonType.Opacity:
                            b.Path = new PropertyPath("Valued");
                            exs[i].Valued = 0d;
                            b.Source = exs[i];
                            fes[i].SetBinding(FrameworkElement.OpacityProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Da, new PropertyPath("Opacity"));
                            Storyboard.SetTarget(exs[i].Da, fes[i]);
                            break;
                        case CartoonType.Width:
                            b.Path = new PropertyPath("Valued");
                            exs[i].Valued = 0d;
                            b.Source = exs[i];
                            fes[i].SetBinding(FrameworkElement.WidthProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Da, new PropertyPath("Width"));
                            Storyboard.SetTarget(exs[i].Da, fes[i]);
                            break;
                        case CartoonType.Height:
                            b.Path = new PropertyPath("Valued");
                            exs[i].Valued = 0d;
                            b.Source = exs[i];
                            fes[i].SetBinding(FrameworkElement.HeightProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Da, new PropertyPath("Height"));
                            Storyboard.SetTarget(exs[i].Da, fes[i]);
                            break;
                        case CartoonType.Background:
                            b.Path = new PropertyPath("Valuec");
                            exs[i].Valuec = Colors.Transparent;
                            b.Source = exs[i];
                            fes[i].SetBinding(Control.BackgroundProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Ca, new PropertyPath("(FrameworkElement.Background).(SolidColorBrush.Color)"));
                            Storyboard.SetTarget(exs[i].Ca, fes[i]);
                            break;
                        case CartoonType.Fill:
                            b.Path = new PropertyPath("Valuec");
                            exs[i].Valuec = Colors.Transparent;
                            b.Source = exs[i];
                            fes[i].SetBinding(Shape.FillProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Ca, new PropertyPath("(FrameworkElement.Fill).(SolidColorBrush.Color)"));
                            Storyboard.SetTarget(exs[i].Ca, fes[i]);
                            break;
                        case CartoonType.Stroke:
                            b.Path = new PropertyPath("Valuec");
                            exs[i].Valuec = Colors.Transparent;
                            b.Source = exs[i];
                            fes[i].SetBinding(Shape.StrokeProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Ca, new PropertyPath("(FrameworkElement.Stroke).(SolidColorBrush.Color)"));
                            Storyboard.SetTarget(exs[i].Ca, fes[i]);
                            break;
                        case CartoonType.Foreground:
                            b.Path = new PropertyPath("Valuec");
                            exs[i].Valuec = Colors.Transparent;
                            b.Source = exs[i];
                            fes[i].SetBinding(Control.ForegroundProperty, b);
                            Storyboard.SetTargetProperty(exs[i].Ca, new PropertyPath("(FrameworkElement.Foreground).(SolidColorBrush.Color)"));
                            Storyboard.SetTarget(exs[i].Ca, fes[i]);
                            break;
                    }
                    exs[i].SetArgument(tla);
                    sb.Children.Add(exs[i]);
                }
            } else if (tla.cartoonType == CartoonType.Transform) {
                var ctx = "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateX)";
                var cty = "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateY)";
                var cskx = "(FrameworkElement.RenderTransform).(CompositeTransform.SkewX)";
                var csky = "(FrameworkElement.RenderTransform).(CompositeTransform.SkewY)";
                var cscx = "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleX)";
                var cscy = "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleY)";
                var cr = "(FrameworkElement.RenderTransform).(CompositeTransform.Rotation)";
                var ccx = "(FrameworkElement.RenderTransform).(CompositeTransform.CenterX)";
                var ccy = "(FrameworkElement.RenderTransform).(CompositeTransform.CenterY)";
                exs = new Entityx[9 * len];
                for (int i = 0; i < len; i++) {
                    var idx = 0; fes[i].RenderTransform = new CompositeTransform();
                    SetEntityxTransform(ref tla, fes, sb, exs, ctx, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, cty, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, cskx, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, csky, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, cscx, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, cscy, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, cr, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, ccx, i, idx++);
                    SetEntityxTransform(ref tla, fes, sb, exs, ccy, i, idx++);
                }
            } else if (tla.cartoonType == CartoonType.Projection) {
                var ppcrx = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationX)";
                var ppcry = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationY)";
                var ppcrz = "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationZ)";
                var ppgx = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetX)";
                var ppgy = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetY)";
                var ppgz = "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetZ)";
                var pplx = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetX)";
                var pply = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetY)";
                var pplz = "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetZ)";
                var pprx = "(FrameworkElement.Projection).(PlaneProjection.RotationX)";
                var ppry = "(FrameworkElement.Projection).(PlaneProjection.RotationY)";
                var pprz = "(FrameworkElement.Projection).(PlaneProjection.RotationZ)";
                exs = new Entityx[12 * len];
                for (int i = 0; i < len; i++) {
                    var idx = 0; fes[i].Projection = new PlaneProjection();
                    SetEntityxProjection(ref tla, fes, sb, exs, ppcrx, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppcry, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppcrz, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppgx, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppgy, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppgz, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, pplx, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, pply, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, pplz, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, pprx, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, ppry, i, idx++);
                    SetEntityxProjection(ref tla, fes, sb, exs, pprz, i, idx++);
                }
            }
            var ea = new EntityxsAction(exs);
            ea.Storyboard = sb;
            return ea;
        }
        static void SetEntityxTransform(ref TlArgument tla, FrameworkElement[] fes, Storyboard sb, Entityx[] exs, string transformPath, int i, int idx) {
            var iidx = i * 9 + idx;
            var name = string.IsNullOrEmpty(fes[i].Name) ? string.Format("p{0}", i) : fes[i].Name;
            exs[iidx] = new Entityx { groupName = name, groupIndex = PpMapping(transformPath), groupIdx = idx };
            exs[iidx].Valued = 0d;
            Storyboard.SetTargetProperty(exs[iidx].Da, new PropertyPath(transformPath));
            Storyboard.SetTarget(exs[iidx].Da, fes[i]);
            exs[iidx].SetArgument(tla); sb.Children.Add(exs[iidx]);
        }
        static void SetEntityxProjection(ref TlArgument tla, FrameworkElement[] fes, Storyboard sb, Entityx[] exs, string projectionPath, int i, int idx) {
            var iidx = i * 12 + idx;
            var name = string.IsNullOrEmpty(fes[i].Name) ? string.Format("p{0}", i) : fes[i].Name;
            exs[iidx] = new Entityx { groupName = name, groupIndex = PpMapping(projectionPath), groupIdx = idx };
            exs[iidx].Valued = 0d;
            Storyboard.SetTargetProperty(exs[iidx].Da, new PropertyPath(projectionPath));
            Storyboard.SetTarget(exs[iidx].Da, fes[i]);
            exs[iidx].SetArgument(tla); sb.Children.Add(exs[iidx]);
        }
        static string PpMapping(string x) {
            var r = string.Empty;
            switch (x) {
                case "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateX)": r = "ctx"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.TranslateY)": r = "cty"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.SkewX)": r = "ctskx"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.SkewY)": r = "ctsky"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleX)": r = "ctscx"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.ScaleY)": r = "ctscy"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.Rotation)": r = "ctr"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.CenterX)": r = "ctcx"; break;
                case "(FrameworkElement.RenderTransform).(CompositeTransform.CenterY)": r = "ctcy"; break;

                case "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationX)": r = "ppcrx"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationY)": r = "ppcry"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.CenterOfRotationZ)": r = "ppcrz"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetX)": r = "ppgx"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetY)": r = "ppgy"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.GlobalOffsetZ)": r = "ppgz"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetX)": r = "pplx"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetY)": r = "pply"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.LocalOffsetZ)": r = "pplz"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.RotationX)": r = "pprx"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.RotationY)": r = "ppry"; break;
                case "(FrameworkElement.Projection).(PlaneProjection.RotationZ)": r = "pprz"; break;
            }
            return r;
        }
    }
}
